package utility;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;

public class AutoCompleter {
	JList list = new JList();
	JPopupMenu popup = new JPopupMenu();
	JTextComponent textComp;
	Vector<String> listAttributeNames;
	private static final String AUTOCOMPLETER = "AUTOCOMPLETER";

	public AutoCompleter(JTextComponent comp) {
		list.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				acceptedListItem((String) ((JList) e.getSource())
						.getSelectedValue());
				popup.setVisible(false);
				
			}
		});
		textComp = comp;
		textComp.putClientProperty(AUTOCOMPLETER, this);
		JScrollPane scroll = new JScrollPane(list);
		scroll.setBorder(null);

		list.setFocusable(false);
		scroll.getVerticalScrollBar().setFocusable(false);
		scroll.getHorizontalScrollBar().setFocusable(false);

		popup.setBorder(BorderFactory.createLineBorder(Color.black));
		popup.add(scroll);

		if (textComp instanceof JTextField) {
			textComp.registerKeyboardAction(showAction,
					KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
					JComponent.WHEN_FOCUSED);
			textComp.getDocument().addDocumentListener(documentListener);
		} else
			textComp.registerKeyboardAction(showAction, KeyStroke
					.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK),
					JComponent.WHEN_FOCUSED);

		textComp.registerKeyboardAction(upAction,
				KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
				JComponent.WHEN_FOCUSED);
		textComp.registerKeyboardAction(hidePopupAction,
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_FOCUSED);

		popup.addPopupMenuListener(new PopupMenuListener() {
			public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
			}

			public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
				textComp.unregisterKeyboardAction(KeyStroke.getKeyStroke(
						KeyEvent.VK_ENTER, 0));
			}

			public void popupMenuCanceled(PopupMenuEvent e) {
			}
		});
		list.setRequestFocusEnabled(false);
	}

	Action acceptAction = new AbstractAction() {
		public void actionPerformed(ActionEvent e) {
			JComponent tf = (JComponent) e.getSource();
			AutoCompleter completer = (AutoCompleter) tf
					.getClientProperty(AUTOCOMPLETER);
			completer.popup.setVisible(false);
			completer.acceptedListItem((String)completer.list.getSelectedValue());
		}
	};

	DocumentListener documentListener = new DocumentListener() {
		public void insertUpdate(DocumentEvent e) {
			showPopup();
		}

		public void removeUpdate(DocumentEvent e) {
			showPopup();
		}

		public void changedUpdate(DocumentEvent e) {
		}
	};

	private void showPopup() {
		popup.setVisible(false);
		if (textComp.isEnabled() && updateListData() && list.getModel().getSize() != 0) {
			if (!(textComp instanceof JTextField))
						textComp.getDocument()
						.addDocumentListener(documentListener);
			textComp.registerKeyboardAction(acceptAction,
					KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
					JComponent.WHEN_FOCUSED);
			int size = list.getModel().getSize();
			list.setVisibleRowCount(size < 10 ? size : 10);

			int x = 0;
			try {
				int pos = Math.min(textComp.getCaret().getDot(), textComp
						.getCaret().getMark());
				x = textComp.getUI().modelToView(textComp, pos).x;
			} catch (BadLocationException e) {
				// this should never happen!!!
				e.printStackTrace();
			}
			popup.show(textComp, x, textComp.getHeight());
		} else
			popup.setVisible(false);
		textComp.requestFocus();
	}

	Action showAction = new AbstractAction() {
		public void actionPerformed(ActionEvent e) {
			JComponent tf = (JComponent) e.getSource();
			AutoCompleter completer = (AutoCompleter) tf
					.getClientProperty(AUTOCOMPLETER);
			if (tf.isEnabled()) {
				if (completer.popup.isVisible())
					completer.selectNextPossibleValue();
				else
					completer.showPopup();
			}
		}
	};

	Action upAction = new AbstractAction() {
		public void actionPerformed(ActionEvent e) {
			JComponent tf = (JComponent) e.getSource();
			AutoCompleter completer = (AutoCompleter) tf
					.getClientProperty(AUTOCOMPLETER);
			if (tf.isEnabled()) {
				if (completer.popup.isVisible())
					completer.selectPreviousPossibleValue();
			}
		}
	};

	Action hidePopupAction = new AbstractAction() {
		public void actionPerformed(ActionEvent e) {
			JComponent tf = (JComponent) e.getSource();
			AutoCompleter completer = (AutoCompleter) tf
					.getClientProperty(AUTOCOMPLETER);
			if (tf.isEnabled())
				completer.popup.setVisible(false);
		}
	};

	/**
	 * Selects the next item in the list. It won't change the selection if
	 * the currently selected item is already the last item.
	 */
	protected void selectNextPossibleValue() {
		int si = list.getSelectedIndex();
		if (si < list.getModel().getSize() - 1) {
			list.setSelectedIndex(si + 1);
			list.ensureIndexIsVisible(si + 1);
		}
	}

	/**
	 * Selects the previous item in the list. It won't change the selection
	 * if the currently selected item is already the first item.
	 */
	protected void selectPreviousPossibleValue() {
		int si = list.getSelectedIndex();
		if (si > 0) {
			list.setSelectedIndex(si - 1);
			list.ensureIndexIsVisible(si - 1);
		}
	}
	
	protected void acceptedListItem(String selected){ 
        if(selected == null) {return;} 
        String value = textComp.getText();
        int index = value.lastIndexOf(".");
        if(index == -1) {return;}
        
        int prefixlen = textComp.getDocument().getLength() - index - 1;
        
        textComp.setText(textComp.getText() + selected.substring(prefixlen));
    } 
	
	protected boolean updateListData(){ 
        String value = textComp.getText(); 
        
        int indexDot = value.lastIndexOf(".");
        if(indexDot == -1) {return false;}
        int indexComma = value.lastIndexOf(",");
        if(indexComma > indexDot) {return false;}
        
        Vector<String> vListAtt = new Vector<String>();
        
        String tableName = value.substring(indexComma + 1, indexDot);
        String attName = value.substring(indexDot + 1);
        tableName = tableName.trim();
        attName = attName.trim();
        
        for(String str : listAttributeNames) {
        	if(
        			matchPrefix(tableName, str) && 
        			matchPrefix(
        							attName, 
        							str.substring(str.lastIndexOf(".") + 1)
        						)
        		) {
        		
        		vListAtt.add(str.substring(str.lastIndexOf(".") + 1));
        	}
        }
        list.setListData(vListAtt);
        return true;
    } 
	/**
	 * if compare has prefix patern then return true. otherwise return false
	 * @param patern
	 * @param compare
	 * @return
	 */
	private boolean matchPrefix(String patern, String compare) {
		char[] _patern = patern.toCharArray();
		char[] _compare = compare.toCharArray();
		int len = _patern.length < _compare.length ? _patern.length : _compare.length;
		for(int i = 0; i < len; i++) {
			if(_patern[i] != _compare[i]) {
				return false;
			}
		}
		
		return true;
	}
}
